package com.alogic.xscript.cert;

import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsArray;
import com.alogic.xscript.doc.XsObject;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import com.anysoft.util.code.util.ByteUtil;
import org.apache.commons.lang3.StringUtils;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.security.auth.x500.X500Principal;
import java.security.PublicKey;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * 查询出证书所有信息
 *
 * @version 1.6.13.32 [20210128 duanyy] <br>
 * - 证书增加SAN信息输出
 */
public class CertQueryAll extends CertQuery{
    protected String $tag;
    protected static final String [] sanTypes = new String[]{
            "otherName","rfc822Name","dNSName","x400Address","directoryName","ediPartyName","uniformResourceIdentifier",
            "iPAddress","registeredID"
    };

    public CertQueryAll(String tag, Logiclet p) {
        super(tag, p);
    }

    @Override
    public void configure(Properties props){
        super.configure(props);
        $tag = PropertiesConstants.getRaw(props,"tag","");
    }

    @Override
    protected void onExecute(X509Certificate cert, XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
        XsObject parent = current;
        String tag = PropertiesConstants.transform(ctx,$tag,"");
        if (StringUtils.isNotEmpty(tag)){
            parent = current.getObjectChild(tag,true);
        }

        log("hello");

        parent.addProperty("type",cert.getType());
        parent.addProperty("sn",ByteUtil.byte2string(cert.getSerialNumber().toByteArray(),true,true));
        parent.addProperty("version",cert.getVersion());
        parent.addProperty("signAlgorithm",cert.getSigAlgName());
        parent.addProperty("signAlgorithmOID",cert.getSigAlgOID());
        parent.addProperty("notBefore",cert.getNotBefore().getTime());
        parent.addProperty("notAfter",cert.getNotAfter().getTime());

        byte[] algPara = cert.getSigAlgParams();
        if (algPara != null){
            parent.addProperty("signAlgorithmPara",ByteUtil.byte2string(algPara,true,true));
        }

        byte[] signature = cert.getSignature();
        if (signature != null) {
            parent.addProperty("signature", ByteUtil.byte2string(signature, true, true));
        }

        boolean[] baseUsage = cert.getKeyUsage();
        if (baseUsage != null && baseUsage.length >= 9){
            XsObject usage = current.getObjectChild("usage",true);
            usage.addProperty("digitalSignature",baseUsage[0]);
            usage.addProperty("nonRepudiation",baseUsage[1]);
            usage.addProperty("keyEncipherment",baseUsage[2]);
            usage.addProperty("dataEncipherment",baseUsage[3]);
            usage.addProperty("keyAgreement",baseUsage[4]);
            usage.addProperty("keyCertSign",baseUsage[5]);
            usage.addProperty("cRLSign",baseUsage[6]);
            usage.addProperty("encipherOnly",baseUsage[7]);
            usage.addProperty("decipherOnly",baseUsage[8]);
        }

        X500Principal issuer = cert.getIssuerX500Principal();
        if (issuer != null){
            XsObject issuerObject = parent.getObjectChild("issuer",true);
            String name = issuer.getName(X500Principal.RFC2253);
            issuerObject.addProperty("raw",name);
            try {
                LdapName ldapName = new LdapName(name);
                List<Rdn> list = ldapName.getRdns();
                for (Rdn rdn:list){
                    issuerObject.addProperty(rdn.getType(), rdn.getValue().toString());
                }

                Collection<List<?>> subjectAltNames = cert.getIssuerAlternativeNames();
                if (subjectAltNames != null) {
                    XsArray array = issuerObject.getArrayChild("sans", true);
                    Iterator<List<?>> iter = subjectAltNames.iterator();
                    while (iter.hasNext()) {
                        List<?> l = iter.next();
                        if (l.size() != 2) {
                            continue;
                        }
                        XsObject san = array.newObject();
                        san.addProperty("type", getSanTypeName(l.get(0).toString()));
                        san.addProperty("value", l.get(1).toString());
                        array.add(san);
                    }
                }
            } catch (InvalidNameException e) {
                log("issuer name of x509 error.","error");
            }catch (CertificateParsingException e) {
                log("subject alt names of x509 error.","error");
            }
        }

        X500Principal self = cert.getSubjectX500Principal();
        if (self != null){
            XsObject subjectObject = parent.getObjectChild("subject",true);
            String name = self.getName(X500Principal.RFC2253);
            subjectObject.addProperty("raw",name);
            try {
                LdapName ldapName = new LdapName(name);
                List<Rdn> list = ldapName.getRdns();
                for (Rdn rdn:list){
                    subjectObject.addProperty(rdn.getType(), rdn.getValue().toString());
                }

                Collection<List<?>> subjectAltNames = cert.getSubjectAlternativeNames();
                if (subjectAltNames != null) {
                    XsArray array = subjectObject.getArrayChild("sans", true);
                    Iterator<List<?>> iter = subjectAltNames.iterator();
                    while (iter.hasNext()) {
                        List<?> l = iter.next();
                        if (l.size() != 2) {
                            continue;
                        }
                        XsObject san = array.newObject();
                        san.addProperty("type", getSanTypeName(l.get(0).toString()));
                        san.addProperty("value", l.get(1).toString());
                        array.add(san);
                    }
                }
            } catch (InvalidNameException e) {
                log("subject name of x509 error.","error");
            } catch (CertificateParsingException e) {
                log("subject alt names of x509 error.","error");
            }
        }


        PublicKey publicKey = cert.getPublicKey();
        if (publicKey != null){
            XsObject pkObject = parent.getObjectChild("public",true);
            pkObject.addProperty("algorithm",publicKey.getAlgorithm());
            pkObject.addProperty("format",publicKey.getFormat());
            pkObject.addProperty("content",ByteUtil.byte2string(publicKey.getEncoded(),true,true));
        }

    }

    public static String getSanTypeName(String type){
        int index = 0;
        try{
            index = Integer.parseInt(type);
        }catch (NumberFormatException ex){

        }
        if (index <0 || index >= sanTypes.length){
            index = 0;
        }
        return sanTypes[index];
    }
}
